<!--
  - Copyright (c) 2018.
  - 北京九思金信科技有限公司对该文件内容保留所有权利。
  - 未经许可，不得私自拷贝、传播、发布、引用该文件内容，违者将被追究法律责任。
  -->

<template>
  <div class="ft-expression">
    <div class="ft-expression__editor">
      <!--公式编辑框-->
      <textarea class="ft-expression__input" ref="input" spellcheck="false" v-model="expression"></textarea>
      <!--运算符面板-->
      <div class="ft-expression__operator">
        <el-row>
          <el-col style="text-align: center;padding: 4px 0" :span="8" v-for="oper in operators" :key="oper.name">
            <el-button style="padding: 7px" :icon="oper.icon" :title="oper.name"
                       @click="inputOperator(oper)"></el-button>
          </el-col>
        </el-row>
      </div>
    </div>
    <el-alert show-icon v-if="syntaxError" :title="syntaxError" type="error" :closable="false"></el-alert>
    <!--关键字面板-->
    <el-tabs class="ft-expression-keywords" type="border-card">
      <el-tab-pane label="变量">
        <el-button v-for="(v,index) in variables" :key="v.messageKeyId" type="text" @click="inputVariable(v)">
          <span style="font-weight: bold;margin-right: 10px">{{index+1}}.</span>
          <span>{{v.keyName}}</span>
          <span>({{v.keyCode}})</span>
        </el-button>
      </el-tab-pane>
      <el-tab-pane label="函数">
        <el-collapse accordion>
          <el-collapse-item v-for="funcGroup in funcGroups" :key="funcGroup.groupCode"
                            :title="funcGroup.groupName+'（'+funcGroup.groupCode+'）'">
            <el-button type="text" v-for="func in funcGroup.funcList" :key="func.funcCode" :title="func.funcName"
                       @click="inputFunction(funcGroup,func)">
              {{func.funcCode}}
            </el-button>
          </el-collapse-item>
        </el-collapse>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>

<script>
  import clone from 'clone'

  const el = {
    empty: {
      pattern: /^[ \n]*/
    },
    comma: {
      pattern: /^ *,/
    },
    colon: {
      pattern: /^ *:/
    },
    leftParenthesis: {
      pattern: /^ *\(/
    },
    rightParenthesis: {
      pattern: /^ *\)/
    },
    immediateValue: {
      pattern: /^ *(([\+\-]?((([0]|([1-9][0-9]*))\.[0-9]+)|(true)|(false)|([0]|([1-9][0-9]*))))|(['][^']*[']))/
    },
    binaryOperator: {
      pattern: /^ *((!=)|(==)|(>=)|(<=)|[\+\-\*\/><])/
    },
    questionMark: {
      pattern: /^ *\?/
    },
    variable: {
      pattern: null
    },
    func: {
      pattern: null
    }
  }
  const getValue = (exp) => {
    // return '${' + exp + '}'
    return  exp
  }
  const getExpression = (val) => {
    const exp = val && val.indexOf('${') == 0 && val.indexOf('}') == val.length - 1 ? val.substr(2, val.length - 3) : val
    return exp || ''
  }
  const getSelectedRange = (input) => {
    var range = null;
    if (typeof input.selectionStart === 'number' && typeof input.selectionEnd === 'number') {
      // 非IE浏览器
      console.log('非IE')
      range = {start: input.selectionStart, end: input.selectionEnd}
    } else if (document.selection) {
      // IE浏览器
      console.log('IE')
      range = document.selection
    }
    console.log(range)
    return range
  }
  export default {
    name: "FtExpression",
    props: ['value', 'variables', 'functions'],
    created() {
      this.$nextTick(() => {
        this.expression = getExpression(this.value)
        this.initVariablePatterns()
        this.initFunctionPatterns()
      })
    },
    data() {
      return {
        expression: null,
        operators: [
          {name: '加', symbol: '+', icon: 'joys-icon-plus'},
          {name: '减', symbol: '-', icon: 'joys-icon-subtraction'},
          {name: '乘', symbol: '*', icon: 'joys-icon-multiplication'},
          {name: '除', symbol: '/', icon: 'joys-icon-division'},
          {name: '等于', symbol: '==', icon: 'joys-icon-equal'},
          {name: '不等于', symbol: '!=', icon: 'joys-icon-noequal'},
          {name: '大于', symbol: '>', icon: 'joys-icon-more'},
          {name: '小于', symbol: '<', icon: 'joys-icon-less'},
          {name: '大于等于', symbol: '>=', icon: 'joys-icon-moreorequal'},
          {name: '小于等于', symbol: '<=', icon: 'joys-icon-lessorequal'},
          {name: '或', symbol: '||', icon: 'joys-icon-or'},
          {name: '与', symbol: '&&', icon: 'joys-icon-and'},
          {name: '非', symbol: '!', icon: 'joys-icon-no'},
          {name: '左括号', symbol: '(', icon: 'joys-icon-leftbracket'},
          {name: '右括号', symbol: ')', icon: 'joys-icon-rightbracket'}
        ],
        funcGroups: [
          {
            groupCode: 'Logic',
            groupName: '逻辑运算',
            funcList: [
              {funcCode: 'if( p1, p2, p3 )', funcName: '条件判断'}
            ]
          }, {
            groupCode: 'Math',
            groupName: '算数运算',
            funcList: [
              {funcCode: 'mod( p1, p2 )', funcName: '取模运算'},
              {funcCode: 'pow( p1, p2 )', funcName: '幂运算'}
            ]
          }, {
            groupCode: 'Array',
            groupName: '数组运算',
            funcList: [
              {funcCode: 'indexOf( p1, p2 )', funcName: '取数组中的元素'},
              {funcCode: 'contains( p1, p2 )', funcName: '包含元素判断'}
            ]
          }, {
            groupCode: 'String',
            groupName: '字符运算',
            funcList: [
              {funcCode: 'indexOf( p1, p2 )', funcName: '查找字符串位置'},
              {funcCode: 'substr( p1, p2, p3 )', funcName: '截取字符串'}
            ]
          }
        ],
        syntaxError: null
      }
    },
    methods: {
      inputVariable(v) {
        const textarea = this.$refs.input
        textarea.focus()
        const selectionStart = textarea.selectionStart
        const selectionEnd = textarea.selectionEnd
        this.expression = (this.expression || '')
        this.expression = this.expression.substr(0, selectionStart) + v.keyCode + this.expression.substr(selectionEnd)
        this.$nextTick(() => {
          textarea.setSelectionRange(selectionStart + v.keyCode.length, selectionStart + v.keyCode.length)
          textarea.focus()
        })
      },
      inputFunction(fg, f) {
        this.expression = (this.expression || '') + fg.groupCode + '.' + f.funcCode
      },
      inputOperator(oper) {
        getSelectedRange(this.$refs.input)
        this.expression = (this.expression || '') + oper.symbol
      },
      initVariablePatterns() {
        if (this.variables && this.variables.length > 0) {
          let pattern = '^ *('
          this.variables.forEach((v, index) => {
            pattern += (index > 0 ? '|' : '') + v.keyCode
          })
          pattern += ')'
          el.variable.pattern = new RegExp(pattern)
        } else {
          el.variable.pattern = null
        }
        console.log(el.variable.pattern)
      },
      initFunctionPatterns() {
        if (this.functions && this.functions.length > 0) {
          let pattern = '^ *('
          this.functions.forEach((f, index) => {
            pattern += (index > 0 ? '|' : '') + f.name + '\\('
          })
          pattern += ')'
          el.func.pattern = new RegExp(pattern)
        } else {
          el.func.pattern = null
        }
        console.log(el.func.pattern)
      },
      validate(expression) {
        let exp = clone(expression || '')
        let ret = null
        let preType = null
        //立即数
        let result = el.immediateValue.pattern.exec(exp)
        if (result) {
          preType = 'Expression'
          ret = (ret || '') + result[0]
          const count = result[0].length
          exp = exp.substr(count)
        }
        //变量
        result = el.variable.pattern && el.variable.pattern.exec(exp)
        if (result) {
          if (preType === 'Expression') {
            throw '语法错误'
          }
          preType = 'Expression'
          ret = (ret || '') + result[0]
          const count = result[0].length
          exp = exp.substr(count)
        }
        //函数
        result = el.func.pattern && el.func.pattern.exec(exp)
        if (result) {
          if (preType === 'Expression') {
            throw '语法错误'
          }
          preType = 'Expression'
          ret = (ret || '') + result[0]
          const count = result[0].length
          exp = exp.substr(count)
          result = this.validate(exp)
          result = result && [result]
          if (result) {
            ret = (ret || '') + result[0]
            const count = result[0].length
            exp = exp.substr(count)
            while (true) {
              result = el.comma.pattern.exec(exp)
              if (result) {
                ret = (ret || '') + result[0]
                const count = result[0].length
                exp = exp.substr(count)
                result = this.validate(exp)
                result = result && [result]
                if (result) {
                  ret = (ret || '') + result[0]
                  const count = result[0].length
                  exp = exp.substr(count)
                } else {
                  throw '语法错误'
                }
              } else {
                result = el.rightParenthesis.pattern.exec(exp)
                if (result) {
                  ret = (ret || '') + result[0]
                  const count = result[0].length
                  exp = exp.substr(count)
                  break
                } else {
                  throw '语法错误'
                }
              }
            }
          } else {
            result = el.rightParenthesis.pattern.exec(exp)
            if (result) {
              ret = (ret || '') + result[0]
              const count = result[0].length
              exp = exp.substr(count)
            } else {
              throw '语法错误'
            }
          }
        }
        //括号
        result = el.leftParenthesis.pattern.exec(exp)
        if (result) {
          if (preType === 'Expression') {
            throw '语法错误'
          }
          preType = 'Expression'
          ret = (ret || '') + result[0]
          const count = result[0].length
          exp = exp.substr(count)
          result = this.validate(exp)
          result = result && [result]
          if (result) {
            ret = (ret || '') + result[0]
            const count = result[0].length
            exp = exp.substr(count)
            result = el.rightParenthesis.pattern.exec(exp)
            if (result) {
              ret = (ret || '') + result[0]
              const count = result[0].length
              exp = exp.substr(count)
            } else {
              throw '语法错误'
            }
          } else {
            throw '语法错误'
          }
        }
        //二元运算符
        result = el.binaryOperator.pattern.exec(exp)
        if (result) {
          if (preType !== 'Expression') {
            throw '语法错误'
          }
          preType = 'BinaryOperator'
          ret = (ret || '') + result[0]
          const count = result[0].length
          exp = exp.substr(count)
          result = this.validate(exp)
          if (result) {
            ret = (ret || '') + result
            const count = result.length
            exp = exp.substr(count)
          } else {
            throw '语法错误.'
          }
        }
        //三元运算符
        result = el.questionMark.pattern.exec(exp)
        if (result) {
          if (preType !== 'Expression') {
            throw '语法错误'
          }
          preType = 'QuestionMark'
          ret = (ret || '') + result[0]
          const count = result[0].length
          exp = exp.substr(count)
          result = this.validate(exp)
          result = result && [result]
          if (result) {
            ret = (ret || '') + result[0]
            const count = result[0].length
            exp = exp.substr(count)
            result = el.colon.pattern.exec(exp)
            if (result) {
              ret = (ret || '') + result[0]
              const count = result[0].length
              exp = exp.substr(count)
              result = this.validate(exp)
              result = result && [result]
              if (result) {
                ret = (ret || '') + result[0]
                const count = result[0].length
                exp = exp.substr(count)
              } else {
                throw '语法错误'
              }
            } else {
              throw '语法错误'
            }
          } else {
            throw '语法错误'
          }
        }
        //空格
        result = el.empty.pattern.exec(exp)
        if (result) {
          ret = (ret || '') + result[0]
          const count = result[0].length
          exp = exp.substr(count)
        }
        return ret
      }
    },
    watch: {
      expression(newVal) {
        this.syntaxError = null
        try {
          const result = this.validate(newVal)
          if (result !== newVal) {
            throw '语法错误'
          }
        } catch (error) {
          this.syntaxError = error
        }
        this.$emit('input', getValue(newVal))
      },
      value(newVal) {
        this.expression = getExpression(newVal)
      },
      variables(){
        this.initVariablePatterns()
        this.expression = getExpression(this.value)
        this.initVariablePatterns()
      }
    }
  }
</script>

<style>
  .ft-expression .ft-expression__editor {
    display: flex;
  }

  .ft-expression .ft-expression__editor .ft-expression__input {
    font-size: 14px;
    line-height: 1.5;
    width: calc(100% - 130px);
    height: 187px;
    padding: 10px;
    border-radius: 4px;
    margin-bottom: 15px;
    outline: none;
    resize: none;
  }

  .ft-expression .el-alert {
    margin-bottom: 10px;
  }

  .ft-expression .ft-expression__editor .ft-expression__operator {
    display: inline-block;
    width: 130px;
    padding-left: 5px;
  }

  .ft-expression-keywords {
    /*box-shadow: none;*/
    /*margin-top: 10px;*/
    background: #fff;
    box-shadow: 0 2px 4px 0 rgba(0,0,0,.12), 0 0 6px 0 rgba(0,0,0,.04);
  }

  .ft-expression .ft-expression-keywords .el-tabs__nav-scroll {
    justify-content: left;
  }
</style>
